#include <arch/cpu/soc/irq.h>
#include <irq.h>
#include <errno.h>
#include <init.h>

static unsigned int irq_err_count = 0;
static struct irq_desc irq_descs[NR_IRQS];

static inline void mask_irq(struct irq_desc *desc)
{   
    if (desc->chip->irq_mask) {
        desc->chip->irq_mask(desc);
		irqf_set_masked(desc->flags);
    }   
}

static inline void unmask_irq(struct irq_desc *desc)
{
    if (desc->chip->irq_unmask) {
        desc->chip->irq_unmask(desc);
		irqf_set_unmasked(desc->flags);
    }
}   

static inline void mask_ack_irq(struct irq_desc *desc)
{
    if (desc->chip->irq_mask_ack) {
        desc->chip->irq_mask_ack(desc);
		irqf_set_masked(desc->flags);
    } else {
		if (desc->chip->irq_mask) {
			desc->chip->irq_mask(desc);
			irqf_set_masked(desc->flags);
		}
        if (desc->chip->irq_ack)
            desc->chip->irq_ack(desc);
    }
}

static int ack_bad_irq(unsigned int irq)
{
    irq_err_count++;
	return 0;
}

static void ack_bad(struct irq_desc *desc)
{
	ack_bad_irq(desc->irq);
}   

static struct irq_chip irq_no_chip = {
	.irq_ack		= ack_bad,
};

static void handle_bad_irq(struct irq_desc *desc)
{
    irq_err_count++;
}

int irq_set_handler(unsigned int irq, irq_flow_handler_t handle)
{
    struct irq_desc *desc;

	if (irq >= NR_IRQS) 
		return -EINVAL;

	desc = &irq_descs[irq];

	if (!handle)
		handle = handle_bad_irq;

    desc->handle_irq = handle;
    if (handle == handle_bad_irq) 
		irqf_set_norequest(desc->flags);

	return 0;
}

int irq_set_chip(unsigned int irq, struct irq_chip *chip)
{
	struct irq_desc *desc;

	if (irq >= NR_IRQS) 
		return -EINVAL;

	desc = &irq_descs[irq];

	if (!chip)
		chip = &irq_no_chip;

	desc->chip = chip;

	return 0;
}

int irq_set_flags(unsigned int irq, unsigned int flags)
{       
    struct irq_desc *desc;

	if (irq >= NR_IRQS) 
		return -EINVAL;

	desc = &irq_descs[irq];

	flags &= IRQF_MASK;
	irqf_clr_inprogress(flags);
	irqf_clr_pending(flags);

	desc->flags = flags;

	return 0;
}  

int irq_set_type(unsigned int irq, unsigned int type)
{
	struct irq_desc *desc;
	struct irq_chip *chip;
	unsigned int ret;
	unsigned int unmask = 0;

	if (irq >= NR_IRQS) 
		return -EINVAL;

	desc = &irq_descs[irq];	
	chip = desc->chip;
	if ((!chip) || (!chip->irq_set_type))
		return -EFAULT;

	type &= IRQT_MASK;
	if (!type) 
		return -EINVAL;

	if (irqf_is_set_type_masked(desc->flags)) {
		if (irqf_is_unmasked(desc->flags)) {
			mask_irq(desc);
			unmask = 1;
		}
	}

	ret = chip->irq_set_type(desc, type);

	if (unmask)
		unmask_irq(desc);

	return ret;
}

int generic_handle_irq(unsigned int irq)
{
    struct irq_desc *desc;

	if (irq >= NR_IRQS) 
		return -EINVAL;

	desc = &irq_descs[irq];

	if (desc->handle_irq)
		desc->handle_irq(desc);
		
    return 0;
}  

void do_irq(unsigned int irq)
{
	if (irq >= NR_IRQS) 
		ack_bad_irq(irq);
	 else 
		generic_handle_irq(irq);
}

static int handle_irq_event(struct irq_desc *desc)
{   
    struct irqaction *action = desc->action;
	unsigned int irq = desc->irq;
	int ret;

	irqf_clr_pending(desc->flags);
	irqf_set_inprogress(desc->flags);
	do {
		if (irqaf_is_disabled(action->flags)) 
			local_irq_disable();
		else
			local_irq_enable();
			
		ret = action->handler(irq, action->priv);
		
		action = action->next;
	} while (action);
	local_irq_disable();
	irqf_clr_inprogress(desc->flags);

    return 0;
}

/**
 *  handle_level_irq - Level type irq handler
 *  @irq:   the interrupt number
 *  @desc:  the interrupt description structure for this irq
 *
 *  Level type interrupts are active as long as the hardware line has
 *  the active level. This may require to mask the interrupt and unmask
 *  it after the associated handler has acknowledged the device, so the
 *  interrupt line is back to inactive.
 */
void handle_level_irq(struct irq_desc *desc)
{
    mask_ack_irq(desc);

    if (!desc->action)
		return;

    handle_irq_event(desc);

	unmask_irq(desc);
}

/**
 *  handle_edge_irq - edge type IRQ handler
 *  @irq:   the interrupt number
 *  @desc:  the interrupt description structure for this irq
 *
 *  Interrupt occures on the falling and/or rising edge of a hardware
 *  signal. The occurrence is latched into the irq controller hardware
 *  and must be acked in order to be reenabled. After the ack another
 *  interrupt can happen on the same source even before the first one
 *  is handled by the associated event handler. If this happens it
 *  might be necessary to disable (mask) the interrupt depending on the
 *  controller hardware. This requires to reenable the interrupt inside
 *  of the loop which handles the interrupts which have arrived while
 *  the handler was running. If all pending interrupts are handled, the
 *  loop is left.
 */
void handle_edge_irq(struct irq_desc *desc)
{
    /*
     * If we're currently running this IRQ, 
     * we shouldn't process the IRQ. Mark it pending, handle
     * the necessary masking and go out
     */
    if (irqf_is_inprogress(desc->flags)) {
		irqf_set_pending(desc->flags);
		mask_ack_irq(desc);
		return;
    }

	if (!desc->action) {
		mask_ack_irq(desc);
		return;
	}

    /* Start handling the irq */
    desc->chip->irq_ack(desc);

    do {
        if (irqf_is_pending(desc->flags)) {
            if (irqf_is_masked(desc->flags))
                unmask_irq(desc);
        }

        handle_irq_event(desc);

    } while (irqf_is_pending(desc->flags));
}

void enable_irq(unsigned int irq)
{
	struct irq_desc *desc;

	if (irq >= NR_IRQS) 
		return;

	desc = &irq_descs[irq];	

	if (!desc->chip)
		return;

	if (desc->usage++ == 0) {		
		if (desc->chip->irq_enable) 
			desc->chip->irq_enable(desc);
		if (irqf_is_masked(desc->flags))
			unmask_irq(desc);
	}
}

void disable_irq(unsigned int irq)
{
	struct irq_desc *desc;

	if (irq >= NR_IRQS) 
		return;

	desc = &irq_descs[irq];	

	if (!desc->chip)
		return;
		
	if (--desc->usage == 0) {		
		if (desc->chip->irq_disable) 
			desc->chip->irq_disable(desc);
		if (irqf_is_unmasked(desc->flags))
			mask_irq(desc);	
	}
}

int request_irq(unsigned int irq, struct irqaction *action, irq_handler_t handler, unsigned int flags, void *priv)
{
	struct irq_desc *desc;

	if (irq >= NR_IRQS) 
		return -EINVAL;

	desc = &irq_descs[irq];	

	if (irqf_is_norequest(desc->flags)) 
		return -EINVAL;
	
	if (!action || IS_ERR(action))
		return -EINVAL;
		
	if (!handler || !priv)
		return -EINVAL;

	flags &= IRQAF_MASK;
	
	action->handler = handler;
	action->priv = priv;	
	action->flags = flags;
	action->next = desc->action;
	desc->action = action;

	return 0;	
}

void free_irq(unsigned int irq, void *priv)
{
	struct irq_desc *desc;
    struct irqaction *action, *prev_action;

	if (irq >= NR_IRQS) 
		return;

	desc = &irq_descs[irq];	
	
	if (!priv)
		return;
		
	action = desc->action;
	if (action->priv == priv) {
		desc->action = action->next;
		return;
	}
	prev_action = action;
	action = action->next;
	while (action) {
		if (action->priv == priv) {
			prev_action->next = action->next;
			break;
		}
		action = action->next;
	}
}

static int irq_init(void)
{
	int i;

	for (i = 0; i < NR_IRQS; i++) {
		irq_descs[i].irq = i;
		irq_descs[i].action = NULL;
		irq_descs[i].usage = 0;
	}

	return 0;
}
early0_initcall(irq_init);

